/*
 * reference https://github.com/TylerSmithNet/RESTclient-Android/blob/master/RESTclient/src/com/tylersmith/net
 *
 */

package com.visikard.net;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URLEncoder;
import java.net.UnknownHostException;
import java.util.ArrayList;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
import org.apache.http.NameValuePair;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.HTTP;

import android.util.Log;

import com.visikard.application.Constant;
import com.visikard.application.VKRegApplication;
import com.visikard.utility.HttpsUtils;
import com.visikard.utility.Utils;

public class VkRestClient {

	private boolean authentication = false;
	private ArrayList<NameValuePair> headers;

	private String jsonBody;
	private String message;

	private ArrayList<NameValuePair> params;
	private String response;
	private int responseCode;

	private String url;

	// HTTP Basic Authentication
	private String username;
	private String password;

	public VkRestClient(String url /* , VkBaseUiHandler uiHandler */) {
		this.url = url;
		params = new ArrayList<NameValuePair>();
		headers = new ArrayList<NameValuePair>();
		// outHandler = uiHandler;
	}

	// Be warned that this is sent in clear text, don't use basic auth unless you have to.
	public void addBasicAuthentication(String user, String pass) {
		authentication = true;
		username = user;
		password = pass;
	}

	public void addHeader(String name, String value) {
		headers.add(new BasicNameValuePair(name, value));
	}

	public void addParam(String name, String value) {
		params.add(new BasicNameValuePair(name, value));
	}

	public void addParam(String name, long value) {
		params.add(new BasicNameValuePair(name, String.valueOf(value)));
	}

	public void execute(VkRequestMethod method) throws VKregisterException {

		// check connection
		if (!Utils.hasNetworkConnection(VKRegApplication.instance.getContext())) {
			throw new VKregisterNetworkException("No internet connection!!!");
		}

		try {
			switch (method) {
				case GET: {
					HttpGet request = new HttpGet(url + addGetParams());
					request = (HttpGet) addHeaderParams(request);
					executeRequest(request, url);
					break;
				}
				case POST: {
					HttpPost request = new HttpPost(url);
					request = (HttpPost) addHeaderParams(request);
					request = (HttpPost) addBodyParams(request);
					executeRequest(request, url);
					break;
				}
			}
		}
		catch (ClientProtocolException e) {
			e.printStackTrace();
			throw new VKregisterNetworkException(e.getMessage());
		}
		catch (UnknownHostException e) {
			e.printStackTrace();
			throw new VKregisterNetworkException(e.getMessage());
		}
		catch (IOException e) {
			e.printStackTrace();
			throw new VKregisterException(e.getMessage());
		}
		catch (Exception e) {
			e.printStackTrace();
			throw new VKregisterException(e.getMessage());
		}
	}

	private HttpUriRequest addHeaderParams(HttpUriRequest request) throws Exception {
		for (NameValuePair h : headers) {
			request.addHeader(h.getName(), h.getValue());
		}

		if (authentication) {

			UsernamePasswordCredentials creds = new UsernamePasswordCredentials(username, password);
			request.addHeader(new BasicScheme().authenticate(creds, request));
		}

		return request;
	}

	private HttpUriRequest addBodyParams(HttpUriRequest request) throws Exception {
		if (jsonBody != null) {
			request.addHeader("Content-Type", "application/json");
			if (request instanceof HttpPost)
				((HttpPost) request).setEntity(new StringEntity(jsonBody, "UTF-8"));
			else if (request instanceof HttpPut)
				((HttpPut) request).setEntity(new StringEntity(jsonBody, "UTF-8"));

		}
		else if (!params.isEmpty()) {
			if (request instanceof HttpPost)
				((HttpPost) request).setEntity(new UrlEncodedFormEntity(params, HTTP.UTF_8));
			else if (request instanceof HttpPut)
				((HttpPut) request).setEntity(new UrlEncodedFormEntity(params, HTTP.UTF_8));
		}
		return request;
	}

	private String addGetParams() throws Exception {
		StringBuffer combinedParams = new StringBuffer();
		if (!params.isEmpty()) {
			combinedParams.append("?");
			for (NameValuePair p : params) {
				combinedParams.append((combinedParams.length() > 1 ? "&" : "") + p.getName() + "="
						+ URLEncoder.encode(p.getValue(), "UTF-8"));
			}
		}
		return combinedParams.toString();
	}

	public String getErrorMessage() {
		return message;
	}

	public String getResponse() {
		return response;
	}

	public int getResponseCode() {
		return responseCode;
	}

	public void setJSONString(String data) {
		jsonBody = data;
	}

	private void executeRequest(HttpUriRequest request, String url) throws Exception {
		HttpClient client = null;
		if (HttpVersion.HTTP.compareToIgnoreCase(request.getURI().getScheme()) == 0) {
			// http protocol
			client = new DefaultHttpClient();
		}
		else {
			// https protocol
			client = HttpsUtils.getInstance().httpsClient(new DefaultHttpClient());
		}

		// Setting 30 second timeouts
		HttpParams params = client.getParams();
		HttpConnectionParams.setConnectionTimeout(params, Constant.DEFAULT_TIMEOUT); // 30 * 1000, TODO: const
		HttpConnectionParams.setSoTimeout(params, Constant.DEFAULT_TIMEOUT); // 30 * 1000, TODO: const

		HttpResponse httpResponse;

		try {
			if (VKRegApplication.IS_LOGGING) {
				Log.d(Constant.API_TAG, "API Call: " + url);
				Log.d(Constant.API_TAG, "API Params: " + this.params);
			}

			httpResponse = client.execute(request);
			responseCode = httpResponse.getStatusLine().getStatusCode();
			message = httpResponse.getStatusLine().getReasonPhrase();

			HttpEntity entity = httpResponse.getEntity();

			if (entity != null) {

				InputStream instream = entity.getContent();
				response = convertStreamToString(instream);
				response = response.trim(); // TODO

				// Closing the input stream will trigger connection release
				instream.close();
			}
			if (VKRegApplication.IS_LOGGING)
				Log.d(Constant.API_TAG, "API Response: " + response);

		}
		catch (ClientProtocolException e) {
			e.printStackTrace();
			throw e;
		}
		catch (UnknownHostException e) {
			e.printStackTrace();
			throw e;
		}
		catch (IOException e) {
			e.printStackTrace();
			throw e;
		}
		finally {
			client.getConnectionManager().shutdown(); // TODO
		}
	}

	private static String convertStreamToString(InputStream is) {

		BufferedReader reader = new BufferedReader(new InputStreamReader(is));
		StringBuilder sb = new StringBuilder();

		String line = null;
		try {
			while ((line = reader.readLine()) != null) {
				sb.append(line + "\n");
			}
		}
		catch (IOException e) {
			e.printStackTrace();
		}
		finally {
			try {
				is.close();
			}
			catch (IOException e) {
				e.printStackTrace();
			}
		}
		return sb.toString();
	}
}